package com.chenjl.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.codec.Charsets;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
/**
 * 封装HTTP请求
 * 2016-6-28 14:03:17
 * @author chenjinlong
 */
@Slf4j
public class HttpClientInvoker {
	//设置连接超时时间
	private static final int CONNECT_TIMEOUT = 5000;
	//设置从connect Manager获取Connection 超时时间
	private static final int CONNECTION_REQUEST_TIMEOUT = 5000;
	//请求获取数据的超时时间
	private static final int SOCKET_TIMEOUT = 5000;
	//302，请求的接口被重定向
	
	//post entity 方式
	public static final int POST_JSON_BODY = 1;
	public static final int POST_FORM_BODY = 2;
	
	
	private static final HttpClientInvoker instance = new HttpClientInvoker();
	public static final HttpClientInvoker getInstance() {
		return instance;
	}
	/**
	 * get请求，构建url
	 * @param url
	 * @param datas
	 * @return
	 */
	private static final String buildGetUrl(String url,Map<String,Object> datas) {
		if(datas==null || datas.size()==0) {
			return url;
		}
		
		StringBuilder sb = new StringBuilder(url);
		if(datas.size()>0) {
			sb.append("?");
		}
		
		int count = 0;
		int length = datas.size();
		for(Map.Entry<String,Object> entry : datas.entrySet()) {
			String key = entry.getKey();
			String value = String.valueOf(entry.getValue());
			sb.append(key+"="+value);
			
			count++;
			if(count != length) {
				sb.append("&");
			}
		}
		
		return sb.toString();
	}
	private static final HttpEntity getStringEntity(Map<String,Object> datas) {
		String jsonString = JSONObject.toJSONString(datas);
		StringEntity stringEntity = new StringEntity(jsonString,"UTF-8");
		stringEntity.setContentEncoding("UTF-8");
		stringEntity.setContentType("application/json");
		
		return stringEntity;
	}
	private static final HttpEntity getUrlEncodedFormEntity(Map<String,Object> datas) throws UnsupportedEncodingException {
		List <NameValuePair> nameValuePairs = new ArrayList <NameValuePair>();
		for(Map.Entry<String,Object> entry : datas.entrySet()) {
			nameValuePairs.add(new BasicNameValuePair(entry.getKey(),String.valueOf(entry.getValue())));
		}
		
		return new UrlEncodedFormEntity(nameValuePairs,"UTF-8");
	}
	private static final RequestConfig getRequestConfig() {
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(SOCKET_TIMEOUT)
				.setConnectTimeout(CONNECT_TIMEOUT)
				.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
				.build();
		
		return requestConfig;
	}
	
	
	
	private CloseableHttpClient closeableHttpClient = null;
	private HttpClientInvoker() {
		log.debug("constructor HttpClientInvoker...");
		
		//https
		TrustManager x509TrustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				
			}
			@Override
			public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				
			}
			@Override
			public X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};
		SSLContext sslcontext = null;
		try {
			sslcontext = SSLContext.getInstance("TLS");
			sslcontext.init(null, new TrustManager[]{x509TrustManager},null);
		}
		catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		catch (KeyManagementException e) {
			e.printStackTrace();
		}
		closeableHttpClient = HttpClients.custom()
				.setSSLContext(sslcontext)
				.build();
		
		//http
		//httpClient = HttpClients.createDefault();
        
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
			public void run() {
				destory();
			}
		});
	}
	/**
	 * 同步post方式提交请求
	 * @param url 域名+端口+上下文
	 * @param datas 参数Map
	 * @return 
	 */
	public final HttpResponse syncPost(String url,Map<String,Object> datas,int entryCode) {
		HttpResponse httpResponse = new HttpResponse();
		
		CloseableHttpResponse response = null;
		HttpPost httpPost = new HttpPost(url);

		int statusCode = HttpResponse.SERVICE_ERROR_UNKNOW;
		String responseText = null;
		
        try {
        	HttpEntity postEntity = null;
    		if(entryCode == POST_JSON_BODY) {
    			postEntity = getStringEntity(datas);
    		}
    		else {
    			postEntity = getUrlEncodedFormEntity(datas);
    		}
    		
        	httpPost.setConfig(getRequestConfig());
			httpPost.setEntity(postEntity);
			response = closeableHttpClient.execute(httpPost);
			
			StatusLine statusLine = response.getStatusLine();
			statusCode = statusLine.getStatusCode();
			HttpEntity entity = response.getEntity();
			responseText = EntityUtils.toString(entity,Charsets.UTF_8);
			
			EntityUtils.consume(entity);
		}
        catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
        catch(SocketTimeoutException e) {
        	log.error("服务超时  url: {}, code : {}",url,HttpResponse.SERVICE_TIMEOUT);
        	statusCode = HttpResponse.SERVICE_TIMEOUT;
        }
        catch(SocketException e) {
        	log.error("服务不可用  url: {}, code : {}",url,HttpResponse.SERVICE_NOT_AVAILABLE);
        	statusCode = HttpResponse.SERVICE_NOT_AVAILABLE;
        }
        catch(ConnectTimeoutException e) {
			 log.error("服务链接超时  url: {}, code : {}",url,HttpResponse.SERVICE_TIMEOUT);
			 statusCode = HttpResponse.SERVICE_TIMEOUT;
		 }
        catch (ClientProtocolException e) {
			e.printStackTrace();
		}
        catch (IOException e) {
			e.printStackTrace();
		}
        finally {
        	httpResponse.setStatusCode(statusCode);
			httpResponse.setResponseText(responseText);
			try {
				if(response!=null)
					response.close();
			} 
			catch (IOException e) {
				e.printStackTrace();
			}
		}
        return httpResponse;
	}
	/**
	 * 同步get方式提交请求,  当服务不可用时默认重试3次
	 * @param url 域名+端口+上下文
	 * @param datas 参数Map
	 * @return 
	 */
	public final HttpResponse syncGet(String url,Map<String,Object> datas) {
		HttpResponse httpResponse = new HttpResponse();
		
		url = buildGetUrl(url,datas);
		log.debug("syncGet, url : {}",url);
		
		CloseableHttpResponse response = null;
		HttpGet httpGet = new HttpGet(url);
		httpGet.setConfig(getRequestConfig());
		httpGet.addHeader("Content-Type","text/plain;charset=UTF-8");
		
		int statusCode = HttpResponse.SERVICE_ERROR_UNKNOW;
		String responseText = null;
		try {
			response = closeableHttpClient.execute(httpGet);
			
			StatusLine statusLine = response.getStatusLine();
			statusCode = statusLine.getStatusCode();
			HttpEntity entity = response.getEntity();
			responseText = EntityUtils.toString(entity,Charsets.UTF_8);
			
			httpResponse.setStatusCode(statusCode);
			httpResponse.setResponseText(responseText);
			EntityUtils.consume(entity);
		}
		catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		catch(SocketTimeoutException e) {
        	log.error("服务超时  url: {}, code : {}",url,HttpResponse.SERVICE_TIMEOUT);
        	statusCode = HttpResponse.SERVICE_TIMEOUT;
        }
        catch(SocketException e) {
        	log.error("服务不可用  url: {}, code : {}",url,HttpResponse.SERVICE_NOT_AVAILABLE);
        	statusCode = HttpResponse.SERVICE_NOT_AVAILABLE;
        }
		catch(ConnectTimeoutException e) {
			 log.error("服务链接超时  url: {}, code : {}",url,HttpResponse.SERVICE_TIMEOUT);
			 statusCode = HttpResponse.SERVICE_TIMEOUT;
		 }
        catch (ClientProtocolException e) {
			e.printStackTrace();
		}
        catch (IOException e) {
			e.printStackTrace();
		}
        finally {
        	httpResponse.setStatusCode(statusCode);
			httpResponse.setResponseText(responseText);
			try {
				if(response!=null)
					response.close();
			} 
			catch (IOException e) {
				e.printStackTrace();
			}
		}
		return httpResponse;
	}
	public final void destory() {
		log.debug("HttpClientInvoker destory...");
		try {
			closeableHttpClient.close();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
}